
import os
import time

from copy import deepcopy
from PyPDF2 import PdfFileReader, PdfFileWriter
from reportlab.lib import colors
from reportlab.lib.colors import black
from reportlab.lib.enums import TA_CENTER, TA_LEFT
from reportlab.lib.styles import ParagraphStyle
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.pdfgen import canvas
from reportlab.platypus import Frame, PageTemplate, Paragraph, Table
from reportlab.platypus.doctemplate import BaseDocTemplate, NextPageTemplate

from common.logger import log
from common.benchmark_var import ConfigSections as Sections, ConfigOptions as ConfigOps,\
    MachineOptions as MachineOps, GlobalEnv
from common.machine_info import FULL_MACHINE_INFO_CMD, ExecuteCmd
from framework.report.var_report import JSONFileKey as Key, PdfReportFormat as Pf, \
    ReportContentKey as ReportKey, PdfContentEN as PcEN
from framework.report.pre_report import ReportKeyMapping as KeyMap
from framework.report.base_report import Report


pdfmetrics.registerFont(TTFont(Pf.BOLD, os.path.join(GlobalEnv.GCBS_FONTS_DIR, 'Harmony-Bold.ttf')))
pdfmetrics.registerFont(TTFont(Pf.LIGHT, os.path.join(GlobalEnv.GCBS_FONTS_DIR, 'Harmony-Light.ttf')))
pdfmetrics.registerFont(TTFont(Pf.MEDIUM, os.path.join(GlobalEnv.GCBS_FONTS_DIR, 'Harmony-SemiBold.ttf')))
pdfmetrics.registerFont(TTFont(Pf.REGULAR, os.path.join(GlobalEnv.GCBS_FONTS_DIR, 'Harmony-Regular.ttf')))
pdfmetrics.registerFont(TTFont(Pf.THIN, os.path.join(GlobalEnv.GCBS_FONTS_DIR, 'Harmony-Thin.ttf')))


class PDFReport(Report):
    # framework style
    FRAME_BRAND = Frame(Pf.LR_MARGIN, Pf.MAX_LENGTH - Pf.HEADER - Pf.SUMMARY_HEADER, Pf.BRAND_WIDTH,
                        Pf.SUMMARY_HEADER, leftPadding=0, bottomPadding=0, showBoundary=0,
                        rightPadding=0, topPadding=-5)
    FRAME_REPORT_INFO = Frame(Pf.BRAND_WIDTH + Pf.LR_MARGIN, Pf.MAX_LENGTH - Pf.HEADER - Pf.SUMMARY_HEADER,
                              Pf.REPORT_INFO_WIDTH, Pf.SUMMARY_HEADER, leftPadding=0, bottomPadding=0,
                              showBoundary=0, rightPadding=0, topPadding=0)
    FRAME_SCORE = Frame(Pf.LR_MARGIN, Pf.MAX_LENGTH - Pf.HEADER - Pf.SUMMARY_HEADER - Pf.SCORE,
                        Pf.MAX_WIDTH - 2 * Pf.LR_MARGIN, Pf.SCORE, leftPadding=6, bottomPadding=0,
                        showBoundary=0, rightPadding=6, topPadding=10)
    FRAME_HARDWARE_TITLE = Frame(Pf.LR_MARGIN, Pf.MAX_LENGTH - Pf.HEADER - Pf.SUMMARY_HEADER - Pf.SCORE -
                                 Pf.HARDWARE_TITLE, Pf.MAX_WIDTH - 2 * Pf.LR_MARGIN, Pf.HARDWARE_TITLE, showBoundary=0)
    FRAME_HARDWARE_LEFT = Frame(Pf.LR_MARGIN, Pf.MAX_LENGTH - Pf.HEADER - Pf.SUMMARY_HEADER - Pf.SCORE
                                - Pf.HARDWARE_TITLE - Pf.HARDWARE_LEFT, (Pf.MAX_WIDTH - 2 * Pf.LR_MARGIN) / 2,
                                Pf.HARDWARE_LEFT, leftPadding=0, bottomPadding=6, showBoundary=0,
                                rightPadding=0, topPadding=-5)
    FRAME_HARDWARE_RIGHT = Frame(Pf.MAX_WIDTH / 2, Pf.MAX_LENGTH - Pf.HEADER - Pf.SUMMARY_HEADER - Pf.SCORE
                                 - Pf.HARDWARE_TITLE - Pf.HARDWARE_RIGHT, (Pf.MAX_WIDTH - 2 * Pf.LR_MARGIN) / 2,
                                 Pf.HARDWARE_RIGHT, leftPadding=0, bottomPadding=6, showBoundary=0,
                                 rightPadding=0, topPadding=-5)
    FRAME_SOFTWARE_TITLE = Frame(Pf.LR_MARGIN, Pf.MAX_LENGTH - Pf.HEADER - Pf.SUMMARY_HEADER - Pf.SCORE
                                 - Pf.HARDWARE_TITLE - Pf.HARDWARE_LEFT - Pf.SOFTWARE_TITLE,
                                 Pf.MAX_WIDTH - 2 * Pf.LR_MARGIN, Pf.SOFTWARE_TITLE, showBoundary=0)
    FRAME_SOFTWARE_LEFT = Frame(Pf.LR_MARGIN, Pf.MAX_LENGTH - Pf.HEADER - Pf.SUMMARY_HEADER - Pf.SCORE
                                - Pf.HARDWARE_TITLE - Pf.HARDWARE_LEFT - Pf.SOFTWARE_TITLE - Pf.SOFTWARE_LEFT,
                                (Pf.MAX_WIDTH - 2 * Pf.LR_MARGIN) / 2, Pf.SOFTWARE_LEFT, leftPadding=0,
                                bottomPadding=6, showBoundary=0, rightPadding=5, topPadding=-5)
    FRAME_SOFTWARE_RIGHT = Frame(Pf.MAX_WIDTH / 2, Pf.MAX_LENGTH - Pf.HEADER - Pf.SUMMARY_HEADER - Pf.SCORE
                                 - Pf.HARDWARE_TITLE - Pf.HARDWARE_LEFT - Pf.SOFTWARE_TITLE - Pf.SOFTWARE_RIGHT,
                                 (Pf.MAX_WIDTH - 2 * Pf.LR_MARGIN) / 2, Pf.SOFTWARE_RIGHT, leftPadding=0,
                                 bottomPadding=6, showBoundary=0, rightPadding=0, topPadding=-5)
    FRAME_FIRST_NOTES = Frame(Pf.LR_MARGIN, Pf.BOTTOM_MARGIN, Pf.MAX_WIDTH - 2 * Pf.LR_MARGIN,
                              Pf.MAX_LENGTH - Pf.BOTTOM_MARGIN - Pf.HEADER - Pf.SUMMARY_HEADER - Pf.SCORE,
                              bottomPadding=5, rightPadding=5, topPadding=6, showBoundary=1)
    FRAME_LATER_NOTES = Frame(Pf.LR_MARGIN, Pf.BOTTOM_MARGIN, Pf.MAX_WIDTH - 2 * Pf.LR_MARGIN,
                              Pf.MAX_LENGTH - Pf.TOP_MARGIN - Pf.BOTTOM_MARGIN, leftPadding=5, bottomPadding=5,
                              rightPadding=5, topPadding=6, showBoundary=1)
    # table style
    SUMMARY_HEADER_STYLE = [('TOPPADDING', (0, 0), (-1, -1), 0), ('BOTTOMPADDING', (0, 0), (-1, -1), 0),
                            ('LEFTPADDING', (0, 0), (-1, -1), 0), ('RIGHTPADDING', (0, 0), (-1, -1), 0),
                            ('ALIGN', (0, 0), (-1, -1), 'CENTER')]
    # paragraph style
    BRAND_TABLE = ParagraphStyle(name="Brand", fontName=Pf.REGULAR, fontSize=Pf.BRAND_FS, alignment=TA_CENTER,
                                 leading=Pf.BRAND_LEADING)
    TESTER_TABLE = ParagraphStyle(name="Tester", fontName=Pf.MEDIUM, fontSize=Pf.HS_FS, alignment=TA_CENTER,
                                  leading=Pf.SYS_LEADING)
    SYS_TABLE = ParagraphStyle(name="sys", fontName=Pf.LIGHT, fontSize=Pf.HS_FS, alignment=TA_LEFT,
                               leading=Pf.SYS_LEADING)
    TEST_CASES = ParagraphStyle(name="test_cases", fontName=Pf.REGULAR, fontSize=Pf.TEST_CASE_FS, alignment=TA_LEFT,
                                leading=Pf.TEST_CASE_LEADING)
    SCORE_STYLE = ParagraphStyle(name="suite_score", fontName=Pf.REGULAR, fontSize=Pf.SCORE_FS,
                                 alignment=TA_CENTER, spaceAfter=Pf.CONTENT_SPACE)
    NOTES_TITLE = ParagraphStyle(name="Notes", fontName=Pf.MEDIUM, fontSize=Pf.NOTES_TITLE_FS, alignment=TA_CENTER,
                                 spaceAfter=Pf.NOTES_SPACE)
    STATUS_CONTENT = ParagraphStyle(name="Status", fontName=Pf.MEDIUM, fontSize=Pf.HS_FS, alignment=TA_LEFT,
                                    leftIndent=Pf.STATUS_LEFT_INDIGENT, spaceBefore=Pf.CONTENT_SPACE,
                                    spaceAfter=Pf.BODY_SPACEAFTER)
    PARAGRAPH_BODY = ParagraphStyle(name="ParagraphBody", fontName=Pf.LIGHT, fontSize=Pf.HS_FS, alignment=TA_LEFT,
                                    leftIndent=Pf.PARA_LEFT_INDIGENT, rightIndent=Pf.PARA_RIGHT_INDIGENT,
                                    spaceAfter=Pf.BODY_SPACEAFTER)
    RUN_COPY = ParagraphStyle(name="RunCopy", fontName=Pf.MEDIUM, fontSize=Pf.HS_FS, alignment=TA_LEFT,
                              leftIndent=Pf.RUN_COPY_INDIGENT, spaceAfter=Pf.NOTES_SPACE)
    TAG_CONTENT = ParagraphStyle(name="Tag", fontName=Pf.REGULAR, fontSize=Pf.HS_FS, alignment=TA_LEFT,
                                 spaceAfter=Pf.CONTENT_SPACE)
    HORIZONTAL_LINE = ParagraphStyle(name="HorizontalLine", fontName=Pf.THIN, fontSize=Pf.FS, borderColor=black,
                                     borderWidth=Pf.HORIZONTAL_BORDER_WIDTH, spaceBefore=Pf.HORIZONTAL_SPACE,
                                     spaceAfter=Pf.HORIZONTAL_SPACE, leftIndent=Pf.HORIZONTAL_LEFT_INDIGENT,
                                     rightIndent=Pf.HORIZONTAL_RIGHT_INDIGENT)
    COMPILE_HORIZONTAL = ParagraphStyle(name="Horizontal", fontName=Pf.REGULAR, leftIndent=Pf.COMPILE_LEFT_INDIGENT,
                                        fontSize=Pf.HORIZONTAL_FS, alignment=TA_LEFT, spaceBefore=Pf.BODY_SPACEAFTER)
    COMPILE_MACHINE = ParagraphStyle(name="Machine", fontName=Pf.LIGHT, leftIndent=Pf.COMPILE_LEFT_INDIGENT,
                                        fontSize=Pf.HS_FS, alignment=TA_LEFT, spaceBefore=Pf.BODY_SPACEAFTER)
    COMPILE_TITLE = ParagraphStyle(name="Compiler", fontName=Pf.MEDIUM, fontSize=Pf.HS_FS, alignment=TA_LEFT,
                                   leftIndent=Pf.COMPILE_LEFT_INDIGENT, spaceBefore=Pf.CONTENT_SPACE)
    COMPILE_OPTION = ParagraphStyle(name="Option", fontName=Pf.REGULAR, fontSize=Pf.HS_FS, alignment=TA_LEFT,
                                    leading=Pf.OPTION_LEADING, rightIndent=Pf.OPTION_RIGHT_INDENT)

    def __init__(self, file_name, test_data):
        super().__init__(file_name, test_data)
        self.sys_info = self.test_data.sys_info
        self.hw_info = self.sys_info.get(ReportKey.HARDWARE)
        self.sw_info = self.sys_info.get(ReportKey.SOFTWARE)
        self.score = self.bm_summary.get(Key.SCORE)
        self.flag = self.get_os_lang()
        self.tester = self.bm_summary.get(Key.TESTER_NAME)
        self.test_time = self.bm_summary.get(Key.TEST_TIME)
        self.compiler_version = self.test_data.compile_info.get(ReportKey.COMPILER_VERSION)
        self.compile_option = self.test_data.compile_info.get(ReportKey.COMPILE_OPTION)
        self.total_page = 0
        self.style_title = []
        self.style_body = []
        self.style_footer = []

    def get_report_content(self):
        self.get_summary_header()
        self.get_summary_score()
        self.get_suite_summary()
        self.get_hardware()
        self.get_software()
        self.get_suite_detail()
        self.get_sub_items()
        self.get_compiler_version()
        self.get_compile_option()
        self.get_machine_info()

    @staticmethod
    def get_os_lang():
        lang = os.getenv("LANG", default="en_US.UTF-8").split(".")
        return 1 if lang[0] == ReportKey.ZH_CN else 0   # 1 is chinese and 0 is another

    @staticmethod
    def get_table_content_style(content, content_style, row_length=Pf.DESC_ROW_LENGTH):
        if not content:
            return content
        line_break = True if len(content.encode("utf-8")) > row_length else False
        if line_break:
            content = Paragraph(content.replace("\n", "<br/>"), content_style)
        return content

    def brand_table_model(self, data):
        style = [('FONTNAME', (0, 0), (-1, -1), Pf.REGULAR), ('FONTSIZE', (0, 0), (-1, -1), Pf.BRAND_FS),
                 ('VALIGN', (0, 0), (-1, -1), 'MIDDLE')]
        style.extend(self.SUMMARY_HEADER_STYLE)
        table_body = Table(data, colWidths=Pf.BRAND_WIDTH, rowHeights=Pf.SUMMARY_HEADER, style=style)
        self.story.append(table_body)

    def report_info_table_model(self, data):
        row_heights = Pf.SUMMARY_HEADER / 3
        style = [('FONTNAME', (0, 0), (-1, -1), Pf.MEDIUM), ('FONTSIZE', (0, 0), (0, 0), Pf.REPORT_VERSION_FS),
                 ('FONTSIZE', (0, 1), (0, -1), Pf.HS_FS), ('VALIGN', (-1, -1), (-1, -1), 'MIDDLE')]
        style.extend(self.SUMMARY_HEADER_STYLE)
        table_body = Table(data, colWidths=Pf.REPORT_INFO_WIDTH, rowHeights=row_heights, style=style)
        self.story.append(table_body)

    def get_tester(self, tester):
        if tester.split(":")[-1] == " ":
            tester = "{}{}".format(Pf.TAG_BLOCK * (Pf.TESTER_SPACE_ZH if self.flag else Pf.TESTER_SPACE_EN), tester)
            self.TESTER_TABLE.alignment = TA_LEFT
        return Paragraph(tester, self.TESTER_TABLE)

    def get_summary_header(self):
        brand = self.get_table_content_style(self.sys_info.get(ReportKey.HARDWARE).get(ConfigOps.PRODUCT_MODEL),
                                             self.BRAND_TABLE)
        brand_info = brand if brand else KeyMap.UNKNOWN_TIPS[self.flag]
        self.brand_table_model([[brand_info]])

        report_version, tester, report_date = PcEN.REPORT_VERSION.format(self.bm_summary[Key.VERSION]), \
            KeyMap.TITLE_DICT.get(ReportKey.TESTER_NAME)[self.flag].format(self.tester), \
            KeyMap.TITLE_DICT.get(ReportKey.REPORT_DATE)[self.flag].format(self.test_time)
        tester = self.get_tester(tester)
        self.report_info_table_model([[report_version], [tester], [report_date]])

    def get_summary_score(self):
        copies = self.bm_summary.get(Key.COPIES)
        copies_info = KeyMap.TITLE_DICT[ReportKey.SINGLE_COPY][self.flag].format(copies) if copies == 1 \
            else KeyMap.TITLE_DICT[ReportKey.MULTI_COPIES][self.flag].format(copies)

        #score = self.score if self.score else KeyMap.TITLE_DICT.get(ReportKey.PARTIAL_RUN)[self.flag]
        self.score = "ACE"
        score =self.score
        self.story.append(Paragraph(KeyMap.TITLE_DICT[ReportKey.SUITE_SCORE][self.flag].format(copies_info, score),
                                    self.SCORE_STYLE))

    @staticmethod
    def get_memory(params, env):
        capacity_list, category_list, speed_list, manu_list = \
            env.get(params[0]).split(" | "), env.get(params[1]).split(" | "), \
            env.get(params[2]).split(" | "), env.get(params[3]).split(" | ")

        memory_list, capacity_dict = [], {}
        for index, capacity in enumerate(capacity_list):
            if capacity in capacity_dict:
                capacity_dict[capacity]["count"] += 1
            else:
                capacity_dict[capacity] = {"count": 1, "index": [index]}
        for capacity, value in capacity_dict.items():
            for index in value["index"]:
                if speed_list:
                    memory_list.append("{} * {} {} {} ({})".
                                       format(value["count"], capacity, category_list[index],
                                              speed_list[index], manu_list[index]))
                else:
                    memory_list.append("{} * {} {} ({})".
                                       format(value["count"], capacity, category_list[index], manu_list[index]))

        return ", ".join(memory_list)

    @staticmethod
    def get_storage(params, env):
        os_disk, disk = env.get(params[0]), env.get(params[1])

        if not os_disk or not disk:
            return ""
        for disk_info in disk:
            if disk_info.get(MachineOps.NAME) == os_disk:
                return "{}: {}".format(os_disk, disk_info.get(MachineOps.CAPACITY))
        return ""

    @staticmethod
    def get_file_system(params, env):
        file_system, mount_param = env.get(params[0]), env.get(params[1])
        return "{} ({})".format(file_system, mount_param)

    @staticmethod
    def get_compiler(params, env):
        gcc, gxx = env.get(params[0]), env.get(params[1])
        if gcc == gxx:
            return "gcc/g++ {}".format(gcc)
        else:
            return "gcc {}, g++ {}".format(gcc, gxx)

    def sys_info_table_model(self, data, others_index):
        style = [('TOPPADDING', (0, 0), (-1, -1), 0), ('BOTTOMPADDING', (0, 0), (-1, -1), 0),
                 ('LEFTPADDING', (0, 0), (0, -1), 16), ('LEFTPADDING', (3, 0), (3, -1), 16),
                 ('LEFTPADDING', (1, 0), (2, -1), 6), ('LEFTPADDING', (4, 0), (5, -1), 6),
                 ('RIGHTPADDING', (0, 0), (-1, -1), 0), ('FONTNAME', (0, 0), (-1, -1), Pf.LIGHT),
                 ('FONTNAME', (0, 0), (0, -1), Pf.REGULAR), ('FONTNAME', (3, 0), (3, -1), Pf.REGULAR),
                 ('FONTSIZE', (0, 0), (-1, -1), 10), ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
                 ('VALIGN', (4, others_index), (5, others_index), 'TOP')]
        for n in range(len(data)):
            span_list = [('SPAN', (1, n), (2, n)), ('SPAN', (4, n), (5, n))]
            style.extend(span_list)
        table_body = Table(data, colWidths=Pf.SYS_COL_WIDTH, rowHeights=Pf.SYS_ROW_HEIGHT, style=style)
        self.story.append(table_body)

    def get_sys_info(self, sys_info, map_dict, col_param):
        data_list, others_index = [], 0
        for index, key_map_tuple in enumerate(map_dict.items()):
            sys_key, report_key = key_map_tuple
            result = self.get_table_content_style(sys_info.get(sys_key), self.SYS_TABLE)
            row_info = [report_key[self.flag], result, ""]
            if index < col_param:
                data_list.append(row_info)
            else:
                data_list[index - col_param].extend(row_info)
                others_index += 1
        self.sys_info_table_model(data_list, others_index - 1)
        self.story.append(Paragraph(" ", self.HORIZONTAL_LINE))

    def get_hardware(self):
        process_dict = {MachineOps.MEMORY: {ReportKey.FUNCTION: self.get_memory, ReportKey.PARAMS:
                        [MachineOps.MEMORY_CAPACITY, MachineOps.MEMORY_TYPE, MachineOps.MEMORY_SPEED,
                         MachineOps.MEMORY_MANU]}, MachineOps.STORAGE:
                        {ReportKey.FUNCTION: self.get_storage, ReportKey.PARAMS: [MachineOps.OS_DISK, MachineOps.DISK]}}
        for hw_title, value in process_dict.items():
            self.hw_info[hw_title] = value.get(ReportKey.FUNCTION)(value.get(ReportKey.PARAMS), self.hw_info)
        self.story.append(Paragraph(KeyMap.TITLE_DICT[ReportKey.HARDWARE][self.flag], self.NOTES_TITLE))
        if self.bm_summary.get(Key.MODE) != "Server":
            KeyMap.HW_DICT.pop(ConfigOps.RAID)
        self.get_sys_info(self.hw_info, KeyMap.HW_DICT, Pf.HARDWARE_COL_PARAM)

    def get_software(self):
        process_dict = {MachineOps.FILE_SYSTEM_PARAM: {ReportKey.FUNCTION: self.get_file_system, ReportKey.PARAMS:
                        [MachineOps.FILE_SYSTEM, MachineOps.MOUNT_PARAM]},
                        MachineOps.COMPILER: {ReportKey.FUNCTION: self.get_compiler, ReportKey.PARAMS:
                        [MachineOps.GCC, MachineOps.GXX]}}
        for sw_title, value in process_dict.items():
            self.sw_info[sw_title] = value.get(ReportKey.FUNCTION)(value.get(ReportKey.PARAMS), self.sw_info)

        self.story.append(Paragraph(KeyMap.TITLE_DICT[ReportKey.SOFTWARE][self.flag], self.NOTES_TITLE))
        self.get_sys_info(self.sw_info, KeyMap.SW_DICT, Pf.SOFTWARE_COL_PARAM)
        self.story.append(NextPageTemplate("Later"))

    def set_suite_table_style(self, style):
        self.style_title = [('FONTNAME', (0, 0), (-1, -1), Pf.BOLD), ('FONTSIZE', (0, 0), (-1, -1), 9)]
        self.style_body = [('FONTNAME', (0, 0), (1, -1), Pf.BOLD), ('FONTNAME', (2, 0), (-1, -1), Pf.REGULAR),
                           ('FONTSIZE', (0, 0), (-1, -1), 8)]
        self.style_footer = [('FONTNAME', (0, 0), (-1, -1), Pf.BOLD), ('FONTSIZE', (0, 0), (-1, -1), 8)]
        self.style_title.extend(style)
        self.style_body.extend(style)
        self.style_footer.extend(style)

    def get_testcase_status(self):
        status_info = KeyMap.TITLE_DICT.get(ReportKey.TESTCASE_STATUS)[self.flag]
        result_status = "{}{}".format(KeyMap.TITLE_DICT.get(ReportKey.RESULT_STATUS)[self.flag],
                                      Pf.TAG_BLOCK * Pf.STATUS_SPACE)
        status = KeyMap.TITLE_DICT.get(ReportKey.INVALID)[self.flag] if self.status_num[-1] else\
            KeyMap.TITLE_DICT.get(ReportKey.SUCCESS)[self.flag]
        return status_info.format(result_status, status, *self.status_num), self.status_num[-1]

    def get_suite_summary(self):
        table_data = self.build_summary_table_framework()
        self.summary_table_model(table_data)
        self.story.append(Paragraph(" ", self.HORIZONTAL_LINE))

    def build_summary_table_framework(self):
        test_suites, description, score = KeyMap.TITLE_DICT[ReportKey.TEST_SUITES][self.flag], \
                                          KeyMap.TITLE_DICT[ReportKey.DESC][self.flag], \
                                          KeyMap.TITLE_DICT[ReportKey.SCORE][self.flag]
        table_data = [[test_suites, description, "", score, ""]]
        for suite_name, summary_info in self.bm_suite.items():
            for sub_suite_name, sub_suite_info in summary_info.items():
                if sub_suite_name in [Key.DESC, Key.SCORE]:
                    continue
                desc = self.get_table_content_style(sub_suite_info.get(Key.DESC), self.TEST_CASES)
                result = [sub_suite_name, desc, "", sub_suite_info.get(Key.SCORE), ""]
                table_data.append(result)
            result = [suite_name, summary_info.get(Key.DESC), "", "ACE", ""]
            table_data.append(result)
        return table_data

    def summary_table_model(self, data):
        """
        put testing data into a table
        :param : data, overall data, story
        :return: story
        """
        col_widths = 99  # the width of each col
        row_heights = 20
        style = [('TOPPADDING', (0, 0), (-1, -1), 0), ('BOTTOMPADDING', (0, 0), (-1, -1), 0),
                 ('LEFTPADDING', (0, 0), (0, -1), 0), ('LEFTPADDING', (1, 0), (2, -1), 25),
                 ('RIGHTPADDING', (0, 0), (-1, -1), 0), ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
                 ('ALIGN', (3, 0), (-1, -1), 'RIGHT'), ('RIGHTPADDING', (3, 0), (4, -1), 140)]
        self.set_suite_table_style(style)
        style_title, style_body, style_footer = deepcopy(self.style_title), \
            deepcopy(self.style_body), deepcopy(self.style_footer)
        dis_list = [data[index] for index in range(1, len(data) - 1)]
        if not dis_list:
            dis_list = [[""] * 5]

        for n in range(len(data)):
            span_list = [('SPAN', (1, n), (2, n)), ('SPAN', (3, n), (4, n))]
            style_title.extend(span_list)
            style_body.extend(span_list)
            style_footer.extend(span_list)

        component_table_title = Table([data[0]], colWidths=col_widths, rowHeights=row_heights, style=style_title)
        component_table_body = Table(dis_list, colWidths=col_widths, rowHeights=row_heights, style=style_body)
        component_table_footer = Table([data[-1]], colWidths=col_widths, rowHeights=row_heights, style=style_footer)
        table_horizontal = ParagraphStyle(name="table_hor", borderColor=colors.black, leftIndent=Pf.HORIZONTAL_LEFT,
                                          rightIndent=Pf.HORIZONTAL_RIGHT, borderWidth=Pf.HORIZONTAL_BORDER_WIDTH)
        res_horizontal = ParagraphStyle(name="table_res_hor", fontName=Pf.REGULAR, fontSize=Pf.FS,
                                        leftIndent=Pf.SUMMARY_HORIZONTAL_LEFT, spaceAfter=Pf.BODY_SPACEAFTER)

        self.story.append(component_table_title)
        self.story.append(Paragraph("", table_horizontal))
        self.story.append(component_table_body)
        self.story.append(Paragraph("-" * 63, res_horizontal))
        self.story.append(component_table_footer)
        status_info, fail_flag = self.get_testcase_status()
        if fail_flag:
            self.STATUS_CONTENT.textColor = colors.red
        self.story.append(Paragraph(status_info, self.STATUS_CONTENT))

    def get_run_copy_time(self):
        elapsed_time = round(self.bm_summary[Key.REPORT_TIME] - self.bm_summary[Key.RUN_TIME])
        minutes, seconds = divmod(elapsed_time, 60)
        hours, minutes = divmod(minutes, 60)
        day, hours = divmod(hours, 24)

        start_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(self.bm_summary[Key.RUN_TIME]))
        end_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(self.bm_summary[Key.REPORT_TIME]))
        day_info = KeyMap.TITLE_DICT.get(ReportKey.DAY)[self.flag].format(day)
        hours_info = KeyMap.TITLE_DICT.get(ReportKey.HOUR)[self.flag].format(str(hours).zfill(2))
        minutes_info = KeyMap.TITLE_DICT.get(ReportKey.MINUTE)[self.flag].format(str(minutes).zfill(2))
        seconds_info = KeyMap.TITLE_DICT.get(ReportKey.SECOND)[self.flag].format(str(seconds).zfill(2))

        if day:
            elapsed_time = day_info + hours_info + minutes_info + seconds_info
        elif hours:
            elapsed_time = hours_info + minutes_info + seconds_info
        elif minutes:
            elapsed_time = minutes_info + seconds_info
        else:
            elapsed_time = seconds_info

        run_copy_time = KeyMap.TITLE_DICT.get(ReportKey.PARALLEL_PROCESSES)[self.flag].\
            format(self.bm_summary.get(Key.COPIES), start_time, end_time, elapsed_time)

        self.story.append(Paragraph(run_copy_time, self.RUN_COPY))

    def get_suite_detail(self):
        self.get_run_copy_time()
        test_suites, test_cases, description, seconds, score, unit, index = \
            KeyMap.TITLE_DICT[ReportKey.TEST_SUITES][self.flag], KeyMap.TITLE_DICT[ReportKey.TEST_CASES][self.flag], \
            KeyMap.TITLE_DICT[ReportKey.DESC][self.flag], KeyMap.TITLE_DICT[ReportKey.SECONDS][self.flag], \
            KeyMap.TITLE_DICT[ReportKey.SCORE][self.flag], KeyMap.TITLE_DICT[ReportKey.UNIT][self.flag], \
            KeyMap.TITLE_DICT[ReportKey.INDEX][self.flag]
        title_row = [test_suites, test_cases, description, "", "", seconds, score, unit, index]

        for suite_info in self.bm_data.values():
            self.get_workloads(suite_info, title_row)

    def get_workloads(self, workload_data, table_title=None, sub_item=False):
        horizontal_flag = False
        sub_table_data = [table_title]
        suite_valid, table_data = [], []
        for wd_name, wd_info in workload_data.items():
            wd_dict = wd_info.get(Key.WORKLOADS)
            if not wd_dict:
                continue

            horizontal_flag = True
            if sub_item:
                sub_table_data.append([wd_name, "", "", "", "", "", "", "", ""])
                valid_list = [True]
                suite_name = ""
                wd_score = ["", "", wd_name, "", "", "", "", "", wd_info.get(Key.INDEX)]
            else:
                suite_name = wd_name
                valid_list = []
                wd_score = ["", "", KeyMap.TITLE_DICT.get(ReportKey.SUITE_TOTAL_SCORE)[self.flag].format(wd_name),
                            "", "", "", "", "", wd_info.get(Key.SCORE)]

            for workload_name, workload_info in wd_dict.items():
                desc = self.get_table_content_style(workload_info.get(Key.DESC), self.TEST_CASES)
                if sub_item:
                    result = [suite_name, workload_name, desc, "", "", "", workload_info.get(Key.SCORE),
                              workload_info.get(Key.UNIT), workload_info.get(Key.INDEX)]
                else:
                    result = [suite_name, workload_name, desc, "", "", workload_info.get(Key.ELAPSED_TIME),
                              workload_info.get(Key.SCORE), workload_info.get(Key.UNIT), workload_info.get(Key.INDEX)]
                valid_list.append(workload_info.get(Key.VALID))
                sub_table_data.append(result)
                suite_name = ""
            sub_table_data.append(wd_score)
            table_data.append(sub_table_data)
            sub_table_data = []
            suite_valid.append(valid_list)
        self.suite_detail_table_model(table_data, suite_valid)
        if not sub_item or horizontal_flag:
            self.story.append(Paragraph(" ", self.HORIZONTAL_LINE))

    def get_sub_items(self):
        workloads, sub_items, description, score, unit, index = \
            KeyMap.TITLE_DICT[ReportKey.WORKLOADS][self.flag], KeyMap.TITLE_DICT[ReportKey.SUB_ITEMS][self.flag], \
            KeyMap.TITLE_DICT[ReportKey.DESC][self.flag], KeyMap.TITLE_DICT[ReportKey.SCORE][self.flag],\
            KeyMap.TITLE_DICT[ReportKey.UNIT][self.flag], KeyMap.TITLE_DICT[ReportKey.INDEX][self.flag]

        title_row = [workloads, sub_items, description, "", "", "", score, unit, index]
        self.get_workloads(self.bm_sub_item, title_row, True)

    @staticmethod
    def add_valid_style(style_body, valid_list):
        for index, valid in enumerate(valid_list):
            if not valid:
                valid_style = [('TEXTCOLOR', (1, index), (-1, index), colors.red)]
                style_body.extend(valid_style)

    def suite_detail_table_model(self, table_data, valid_list):
        detail_blank_line = False
        col_widths = Pf.MAX_WIDTH / 10  # the width of each col
        row_heights = 20
        style = [('TOPPADDING', (0, 0), (-1, -1), 0), ('BOTTOMPADDING', (0, 0), (-1, -1), 0),
                 ('LEFTPADDING', (0, 0), (0, -1), 0), ('LEFTPADDING', (2, 0), (4, -1), 25),
                 ('RIGHTPADDING', (0, 0), (-1, -1), 0), ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
                 ('ALIGN', (5, 0), (6, -1), 'RIGHT'), ('ALIGN', (-1, 0), (-1, -1), 'RIGHT'),
                 ('RIGHTPADDING', (5, 0), (6, -1), 25), ('RIGHTPADDING', (-1, 0), (-1, -1), 25)]
        self.set_suite_table_style(style)
        style_title, style_footer = deepcopy(self.style_title), deepcopy(self.style_footer)
        for index, data in enumerate(table_data):
            style_body = deepcopy(self.style_body)
            if detail_blank_line:
                dis_list = [data[index] for index in range(len(data) - 1)]
            else:
                dis_list = [data[index] for index in range(1, len(data) - 1)]
            if not dis_list:
                dis_list = [[""] * 9]
            self.add_valid_style(style_body, valid_list[index])
            for n in range(len(data)):
                span_list = [('SPAN', (2, n), (4, n))]
                style_title.extend(span_list)
                style_body.extend(span_list)
                style_footer.extend(span_list)

            component_table_title = Table([data[0]], colWidths=col_widths, rowHeights=row_heights, style=style_title)
            component_table_body = Table(dis_list, colWidths=col_widths, rowHeights=row_heights, style=style_body)
            component_table_footer = Table([data[-1]], colWidths=col_widths, rowHeights=row_heights, style=style_footer)
            table_horizontal = ParagraphStyle(name="table_hor", borderColor=colors.black,
                                              leftIndent=0, rightIndent=Pf.WL_HORIZONTAL_RIGHT,
                                              borderWidth=Pf.HORIZONTAL_BORDER_WIDTH)
            res_horizontal = ParagraphStyle(name="table_res_hor", fontName=Pf.REGULAR, fontSize=Pf.FS,
                                            leftIndent=Pf.DETAIL_HORIZONTAL_LEFT, spaceAfter=Pf.BODY_SPACEAFTER)
            if detail_blank_line:
                blank_data = [[""] * 9]
                component_table_blank = Table(blank_data, colWidths=col_widths, rowHeights=row_heights,
                                              style=style_body)
                self.story.append(component_table_blank)
            else:
                self.story.append(component_table_title)
                self.story.append(Paragraph("", table_horizontal))
            self.story.append(component_table_body)
            self.story.append(Paragraph("-" * 7, res_horizontal))
            self.story.append(component_table_footer)
            detail_blank_line = True

    def get_compiler_version(self):
        self.story.append(Paragraph(KeyMap.TITLE_DICT[ReportKey.COMPILER_VERSION][self.flag], self.NOTES_TITLE))
        for named_area, area_info in self.compiler_version.items():
            benchmark = "" if named_area == Sections.COMPILER else "{} ".format(named_area)
            for title, info in area_info.items():
                if not info:
                    continue
                module_title = KeyMap.TITLE_DICT.get(title)[self.flag] \
                    if title == MachineOps.JAVA \
                    else KeyMap.TITLE_DICT.get(title)[self.flag].format(benchmark)
                self.story.append(Paragraph(module_title, self.COMPILE_TITLE))
                self.story.append(Paragraph("- " * Pf.HORIZONTAL_SPLIT_LINES, self.COMPILE_HORIZONTAL))
                self.story.append(Paragraph(info.replace("\n", "<br/>"), self.PARAGRAPH_BODY))
        self.story.append(Paragraph(" ", self.HORIZONTAL_LINE))

    def compile_option_table_model(self, data):
        style = [('FONTNAME', (0, 0), (-1, -1), Pf.LIGHT), ('FONTNAME', (1, 0), (4, -1), Pf.REGULAR),
                 ('FONTSIZE', (0, 0), (-1, -1), Pf.HS_FS), ('VALIGN', (0, 0), (-1, -1), 'MIDDLE')]
        for n in range(len(data)):
            span_list = [('SPAN', (1, n), (4, n))]
            style.extend(span_list)
        table_body = Table(data, colWidths=Pf.OPTION_COL_WIDTH, rowHeights=Pf.OPTION_ROW_HEIGHT, style=style)
        self.story.append(table_body)

    def get_compile_option(self):
        self.story.append(Paragraph(KeyMap.TITLE_DICT[ReportKey.COMPILE_OPTION][self.flag], self.NOTES_TITLE))
        for name_area, area_info in self.compile_option.items():
            area_title = KeyMap.TITLE_DICT.get(ReportKey.DEFAULT)[self.flag] \
                if name_area == Sections.COMPILER else name_area
            self.story.append(Paragraph(area_title, self.COMPILE_TITLE))
            self.story.append(Paragraph("- " * Pf.HORIZONTAL_SPLIT_LINES, self.COMPILE_HORIZONTAL))
            table_data = []
            for option_title, option_info in area_info.items():
                if not option_info:
                    continue
                option_info = self.get_table_content_style(option_info, self.COMPILE_OPTION, Pf.OPTION_ROW)
                option_data = [KeyMap.TITLE_DICT.get(option_title)[self.flag], option_info, "", "", ""]
                table_data.append(option_data)
            if table_data:
                self.compile_option_table_model(table_data)

    def get_machine_info(self):
        self.story.append(Paragraph("", self.HORIZONTAL_LINE))
        self.story.append(Paragraph(KeyMap.TITLE_DICT[ReportKey.ENVIORNMENT_INFO][self.flag], self.NOTES_TITLE))
        machine_info = {}
        for cmd in FULL_MACHINE_INFO_CMD:
            res = ExecuteCmd.execute_cmd(cmd)
            machine_info[cmd] = res if res else "Unable to execute {}.".format(cmd)
        self.sys_file = os.path.join(GlobalEnv.GCBS_TMP_DIR, "machine_info.txt")
        with open(self.sys_file, "r") as file:
            line =file.readline()
            while line:
                self.story.append(Paragraph(line, self.COMPILE_MACHINE))
                line=file.readline()
        
    def first_page_design(self, canvas, doc):
        """
        design first page style before add flowables
        :param : canvas object
        :param : doc objecct
        :return:
        """
        self.later_page_design(canvas, doc)
        canvas.rect(Pf.LR_MARGIN, Pf.MAX_LENGTH - Pf.HEADER - Pf.SUMMARY_HEADER, Pf.BRAND_WIDTH,
                    Pf.SUMMARY_HEADER, fill=0)
        canvas.rect(Pf.LR_MARGIN + Pf.BRAND_WIDTH, Pf.MAX_LENGTH - Pf.HEADER - Pf.SUMMARY_HEADER,
                    Pf.REPORT_INFO_WIDTH, Pf.SUMMARY_HEADER, fill=0)
        canvas.rect(Pf.LR_MARGIN, Pf.MAX_LENGTH - Pf.HEADER - Pf.SUMMARY_HEADER - Pf.SCORE,
                    Pf.MAX_WIDTH - 2 * Pf.LR_MARGIN, Pf.SCORE, fill=0)

    def later_page_design(self, canvas, doc):
        """
        design later page style before add flowables
        :param : canvas object
        :param : doc objecct
        :return:
        """
        canvas.setFont(Pf.REGULAR, Pf.HF_FS)
        canvas.drawString(Pf.HEADER_X, Pf.HEADER_Y, KeyMap.TITLE_DICT[ReportKey.HEADER][self.flag].
                          format(self.bm_summary[Key.VERSION], self.test_time))
        logo_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), "logo.png")
        canvas.drawImage(logo_file, Pf.FOOTER_IMG_X, Pf.FOOTER_IMG_Y, width=Pf.IMG_WIDTH, height=Pf.IMG_HEIGHT)
        self.add_watermark(canvas)

    @staticmethod
    def add_watermark(canvas):
        """
        make a watermark pdf
        :param : canvas object
        :return:
        """
        canvas.saveState()
        canvas.setFont(Pf.BOLD, 280)
        watermark_color = colors.HexColor(Pf.WATERMARK_COLOR)
        canvas.setFillColor(watermark_color)
        canvas.rotate(55)
        canvas.drawString(120, -110, "GCBS")
        canvas.restoreState()

    def make_page_info(self, canvas, origin_file):
        self.total_page = PdfFileReader(origin_file).getNumPages()
        for page in range(self.total_page):
            canvas.setFont(Pf.REGULAR, Pf.HS_FS)
            watermark_info = KeyMap.TITLE_DICT.get(ReportKey.FOOTER)[self.flag].format(page + 1, self.total_page)
            canvas.drawString(Pf.FOOTER_STR_X, Pf.FOOTER_STR_Y, watermark_info)
            canvas.showPage()

    def add_page_watermark(self, page_file, origin_file, added_page_file):
        page_reader = PdfFileReader(page_file)

        pdf_reader = PdfFileReader(origin_file)
        pdf_writer = PdfFileWriter()

        for page in range(self.total_page):
            watermark_page = page_reader.getPage(page)
            page_obj = pdf_reader.getPage(page)
            page_obj.mergePage(watermark_page)
            pdf_writer.addPage(page_obj)

        with open(added_page_file, 'wb') as out:
            pdf_writer.write(out)

    def make_report(self):
        """
        create a new pdf
        """
        log.info("Start creating pdf report.")
        try:
            self.get_report_content()
            doc = BaseDocTemplate(self.file_name, showBoundary=0, leftMargin=Pf.LR_MARGIN, rightMargin=Pf.LR_MARGIN,
                                  topMargin=Pf.TOP_MARGIN, bottomMargin=Pf.BOTTOM_MARGIN)
            doc.addPageTemplates([PageTemplate(id='First', frames=[self.FRAME_BRAND, self.FRAME_REPORT_INFO,
                                                                   self.FRAME_SCORE, self.FRAME_FIRST_NOTES],
                                               onPage=self.first_page_design),
                                  PageTemplate(id='Later', frames=[self.FRAME_LATER_NOTES],
                                               onPage=self.later_page_design)])
            doc.multiBuild(self.story)
            page_file = os.path.join(GlobalEnv.GCBS_TMP_DIR, "page.pdf")
            page_data = canvas.Canvas(page_file)
            self.make_page_info(page_data, self.file_name)
            page_data.showPage()
            page_data.save()
            self.add_page_watermark(page_file, self.file_name, self.file_name)
            os.remove(page_file)
            log.info("Create pdf success.")

        except Exception as err:
            log.error("Create pdf fail, %s" % err)
